﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using com.opttek.optquest;

namespace ConsoleApplication1
{
    class KCOptQuest : COptQuestOptimization
    {
        public SimData mysimdata;
        public VariableCube mycube;
        public List<COptQuestBinaryVariable> myvars = new List<COptQuestBinaryVariable>();

        //public Dictionary<string, COptQuestUpperRequirement> myreq = new Dictionary<string, COptQuestUpperRequirement>();
        public ThreadSafeDictionary<string, COptQuestUserControlledVariable> mybomreq = new ThreadSafeDictionary<string, COptQuestUserControlledVariable>();
        
        //Dictionary created for BOM req
        public ThreadSafeDictionary<String, double> myinventories = new ThreadSafeDictionary<String, double>();
        public ThreadSafeDictionary<string, COptQuestUserControlledVariable> mysparetimes = new ThreadSafeDictionary<string, COptQuestUserControlledVariable>();

        public double monitorWindow = 2;
        private double elapsed = 0;

        public COptQuestStringObjective revenueuc = new COptQuestStringObjective();

        public COptQuestUserControlledVariable salesuc = new COptQuestUserControlledVariable("sales");
        public COptQuestUserControlledVariable prodcostuc = new COptQuestUserControlledVariable("prodcost");
        public COptQuestUserControlledVariable setupcostuc = new COptQuestUserControlledVariable("setupcost");
        public COptQuestUserControlledVariable holdcostuc = new COptQuestUserControlledVariable("holdcost");
        public COptQuestUserControlledVariable backordercostuc = new COptQuestUserControlledVariable("backordercost");
        public COptQuestUserControlledVariable tarinvcostuc = new COptQuestUserControlledVariable("tarinvcost");
        public COptQuestUserControlledVariable serviceleveluc = new COptQuestUserControlledVariable("servicelevel");


        //Threading...
        public Queue pendingQ = new Queue();
        public Queue completedQ = new Queue();

        int threadid = 0;

        //This is the amount of parallel threads that will run the program
        public int evaluators;
     //   public int evaluators = 1;
        //public int evaluators = 4;
        ArrayList workers = new ArrayList();
        ArrayList events = new ArrayList();
        ArrayList threads = new ArrayList();


        public KCOptQuest(int license, SimData mydata, int evaluators)
            : base(license)
        {
            mysimdata = mydata;
            mycube = new VariableCube(mydata);
            this.evaluators = evaluators;
            pendingQ = Queue.Synchronized(pendingQ);
            completedQ = Queue.Synchronized(completedQ);
            //Thread.CurrentThread.Name = "Base thread";
            threadid = Thread.CurrentThread.GetHashCode();

            for (int i = 0; i < evaluators; i++)
            {
                events.Add(new ManualResetEvent(false));
                workers.Add(new WorkerThread(this, (ManualResetEvent)events[i], i + 1));
                WorkerThread w = (WorkerThread)workers[i];
                Thread t = new Thread(new ThreadStart(w.work));
                threads.Add(t);
                t.Start();
            }
        }


        override public void Evaluate(COptQuestSolution solution)
        {

            try
            {
                //If the pending solutions are full wait for one to free.
                if (solution == null)
                {
                    WaitHandle.WaitAny((ManualResetEvent[])events.ToArray(typeof(ManualResetEvent)));
                    //  Console.WriteLine("ya termino alguien, desencolo");
                }
                else
                {
                    pendingQ.Enqueue(solution);
                }
                for (int i = 0; i < completedQ.Count; i++)
                {
                    EvaluateComplete((COptQuestSolution)completedQ.Dequeue());
                    //Start the thread if it isnt already


                }

                for (int i = 0; i < threads.Count; i++)
                {
                    Thread t = (Thread)threads[i];
                    if (t.ThreadState == ThreadState.Stopped)
                    {
                        threads.RemoveAt(i);
                        WorkerThread w = (WorkerThread)workers[i];
                        t = new Thread(new ThreadStart(w.work));
                        threads.Insert(i, t);
                        t.Start();
                    }
                    else if (t.ThreadState == ThreadState.Aborted)
                    {
                        throw new Exception("An Unexpected error has ocurred during the optimization");
                    }
                }


            }
            catch (COptQuestException e)
            {
                System.Console.Out.WriteLine(e.Description());
            }

        }

        public COptQuestSolution calcEvaluate(COptQuestSolution solution, int workerpid)
        {
            double sales = 0;
            double prodcost = 0;
            double setupcost = 0;
            double holdcost = 0;
            double backordercost = 0;
            double tarinvcost = 0;
            double servicelevel = 0;


            sales = calcSales(solution);
            solution.SetVariableValue(salesuc, sales);

            prodcost = calcProdCost(solution);
            solution.SetVariableValue(prodcostuc, prodcost);

            setupcost = calcSetup(solution);
            solution.SetVariableValue(setupcostuc, setupcost);

            calcHoldBackCost(solution, out holdcost, out backordercost, out servicelevel);
            //holdcost = calcHoldCost(solution);
            solution.SetVariableValue(holdcostuc, holdcost);
            solution.SetVariableValue(serviceleveluc, servicelevel);
            //backordercost = calcBackCost(solution);
            solution.SetVariableValue(backordercostuc, backordercost);

            tarinvcost = calcTarInvCost(solution);
            solution.SetVariableValue(tarinvcostuc, tarinvcost);

          /*  Console.WriteLine("Sales = " + sales);
            Console.WriteLine("Prodcost = " + prodcost);
            Console.WriteLine("Setupcost = " + setupcost);
            Console.WriteLine("Holdingcost = " + holdcost);
            Console.WriteLine("Backordercost = " + backordercost);
            Console.WriteLine("Targetdeficitcost = " + tarinvcost);*/

            //if (ConsoleApplication1.Properties.Settings.Default.machours)
            //machoursReqcalc(solution);
            //if (ConsoleApplication1.Properties.Settings.Default.bom)
                bomReqcalc(solution);
            //if (ConsoleApplication1.Settings.Default.setup)
            //    setupReqcalc(solution);
                
            return solution;
        }

        override public void MonitorStatus(COptQuestSolution sol)
        {
            try
            {
                long iteration;
                MainProgram p = MainProgram.getInstance(); 
                if (p.useiterations)
                {
                    iteration = sol.GetIteration();


                    if ((iteration % 10) == 0)
                    {
                        //COptQuestSolution bestSol = this.GetBestSolution();
                        //   System.Console.Out.WriteLine("Best solution after " + iteration + " iterations is " + bestSol.GetObjectiveValue());
                        ProgressPacket packet = new ProgressPacket(false);
                        packet.solution = sol;

                        int pr = (int)(((float)iteration / (float)GetMaximumIterations()) * 100);
                        p.myform.worker.ReportProgress(pr, packet);
                    }
                }
                else 
                {
                    TimeSpan now = DateTime.Now - p.basestart;
                    
                    if (now.TotalSeconds  - elapsed >  monitorWindow)
                    {
                        elapsed = now.TotalSeconds;
                        int maxtime = GetMaximumTime();
                        int percentage = (int)((now.TotalMinutes / (float)maxtime) * 100);
                         ProgressPacket packet = new ProgressPacket(false);
                        packet.solution = sol;
                        p.myform.worker.ReportProgress(percentage, packet);

                    }
                }
            }
            catch (COptQuestException e)
            {
                System.Console.Out.WriteLine(e.Description());
            }

        }

        //public void machoursReq()
        //{
        //    COptQuestUpperRequirement machours;
        //    foreach (Machine machine in mysimdata.mymachines.Values)
        //    {
        //        foreach (int epoch in mysimdata.myepochs.Keys)
        //        {
        //            string key = machine.machine + epoch;
        //            machours = new COptQuestUpperRequirement(key, 1);
        //            mymachours.Add(key, machours);
        //        }
        //    }
        //}

        //public void setupReq()
        //{
        //    int i;
        //    COptQuestUpperRequirement req;
        //    foreach (Machine machine in mysimdata.mymachines.Values)
        //        if (machine.materials.Count > 1)
        //            foreach (String matfrom in machine.materials.Keys)
        //                try
        //                {
        //                    foreach (String matto in mysimdata.mysetup[machine.machine][matfrom].Keys)
        //                        try
        //                        {
        //                            for (i = 0; i < (mysimdata.horizon -
        //                                (mysimdata.mysetup[machine.machine][matfrom][matto].setuptime)); i++)
        //                            {
        //                                string key = machine.machine + matfrom + matto + i;
        //                                req = new COptQuestUpperRequirement(key, 1);
        //                                myreq.Add(key, req);
        //                            }
        //                        }
        //                        catch (KeyNotFoundException)
        //                        { }

        //                }
        //                catch (KeyNotFoundException)
        //                { }

        //}

        //public void bomReq()
        //{
        //    int i;
        //    COptQuestLowerRequirement req;
        //    for (i = 1; i < mysimdata.horizon; i++)
        //    {
        //        foreach (string comp in mysimdata.mybomcom.Keys)
        //        {
        //            string key = comp + i;
        //            req = new COptQuestLowerRequirement(key, 1);
        //            mybomreq.MergeSafe(key, req);

        //        }
        //    }
        //}



        //private void machoursReqcalc(COptQuestSolution solution)
        //{
        //    int pid = solution.GetPID();

        //    foreach (Machine machine in mysimdata.mymachines.Values)
        //    {
        //        foreach (Epoch epoch in mysimdata.myepochs.Values)
        //        {
        //            if (machine.nethours[epoch.epoch] < epoch.duration)
        //            {
        //                string key = machine.machine + epoch.epoch;
        //                try
        //                {
        //                    //myrealnethours[key].SetCurrentValue(machine.nethours[epoch] - mysparetimes[machine.machine][epoch]);

        //                    solution.SetVariableValue(myrealnethours[key], machine.nethours[epoch.epoch] - mysparetimes[machine.machine][epoch.epoch]);

        //                }
        //                catch (COptQuestException e)
        //                {
        //                    e.Description();
        //                }

        //            }
        //        }
        //    }
        //}



        //private void setupReqcalc(COptQuestSolution solution)
        //{
        //    int i;
        //    int j;
        //    double calcreq;
        //    double auxisuma = 0;

        //    foreach (Machine machine in mysimdata.mymachines.Values)
        //        if (machine.materials.Count > 1)
        //            foreach (String matfrom in machine.materials.Keys)
        //                try
        //                {
        //                    foreach (String matto in mysimdata.mysetup[machine.machine][matfrom].Keys)
        //                    {
        //                        try
        //                        {
        //                            for (i = 0; i < (mysimdata.horizon -
        //                                (mysimdata.mysetup[machine.machine][matfrom][matto].setuptime) + 1); i++)
        //                            {
        //                                string key = machine.machine + matfrom + matto + i;

        //                                for (j = 1; j < mysimdata.mysetup[machine.machine][matfrom][matto].setuptime + 1; j++)
        //                                {
        //                                    auxisuma += solution.GetVariableValue(mycube.machineRefs[machine.machine][matto][i + j]);
        //                                }
        //                                calcreq = solution.GetVariableValue(mycube.machineRefs[machine.machine][matfrom][i]) * auxisuma;
        //                                solution.SetRequirementValue(myreq[key], calcreq);
        //                                auxisuma = 0;
        //                            }
        //                        }
        //                        catch (KeyNotFoundException)
        //                        {
        //                        }
        //                    }
        //                }
        //                catch (KeyNotFoundException)
        //                {

        //                }

        //}

        private void bomReqcalc(COptQuestSolution solution)
        {
            int i;
            double auxisuma;
            double calc;
            for (i = 0; i < mysimdata.horizon-1; i++)
            {
                foreach (string comp in mysimdata.mybomcom.Keys)
                {
                    auxisuma = 0;
                    calc = 0;
                    string key = "bom" + comp + i;
                    string key3 = comp + i;
                    foreach (string mat in mysimdata.mybomcom[comp].Keys)
                    {
                        foreach (string mach in mysimdata.mymaterials[mat].machines.Keys)
                        {
                            string key2 = mach + mat;
                            double v = mycube.machineRefs[mach][mat][i+1].isMatProducing(solution, mysimdata.mymaterials[mat]);
                            auxisuma += v * mysimdata.mymaterialsprod[key2].prodrate *
                                mysimdata.mybomcom[comp][mat];
                        }
                    }

                        calc = myinventories[key3] - auxisuma;

                        solution.SetVariableValue(mybomreq[key], calc);
    
                }
            }

        }



        private double calcSetup(COptQuestSolution solution)
        {
            double setupcost = 0;
            Dictionary<String, Dictionary<int, StopStartEvent>> mystartstop = new Dictionary<String, Dictionary<int, StopStartEvent>>();
            Dictionary<String, double> exception = new Dictionary<string, double>();
            stopStart(solution, mystartstop, exception);
            double setuptime = 0;
            double stoptime = 0;
            double extra = 0;
            int currentEpoch = 1;
            int prevEpoch = 0;
            string key = "";

            foreach (Machine machine in mysimdata.mymachines.Values)
            {
                foreach (int i in mystartstop[machine.machine].Keys)
                {
                   
                    foreach (int k in mysimdata.myepochs.Keys)
                    {
                        if (i > mysimdata.myepochs[k].start && i <= (mysimdata.myepochs[k].start + mysimdata.myepochs[k].duration))
                        {
                            currentEpoch = k;
                            if (!(prevEpoch == currentEpoch))
                            {
                                setuptime = 0;
                            }
                        }
                    }

                    if (machine.nethours[currentEpoch] < mysimdata.myepochs[currentEpoch].duration)
                    {
                        setuptime += mysimdata.mysetup[machine.machine][mystartstop[machine.machine][i].matfrom][mystartstop[machine.machine][i].matto].setuptime;
                        stoptime += mystartstop[machine.machine][i].start - mystartstop[machine.machine][i].stop;
                        setupcost += mysimdata.mysetup[machine.machine][mystartstop[machine.machine][i].matfrom][mystartstop[machine.machine][i].matto].setupcost;

                        //double vfrom = mycube.machineRefs[machine.machine][matfrom][i].isMatProducing(solution, matfrom);
                        //double vto = mycube.machineRefs[machine.machine][matto][mystartstop[machine][i]].isMatProducing(solution, matto);

                        //setupcost += vfrom * vto * mysimdata.mysetup[machine.machine][matfrom][matto].setupcost;
                        //setuptime += vfrom * vto * mysimdata.mysetup[machine.machine][matfrom][matto].setuptime;

                        /*setupcost += solution.GetVariableValue(mycube.machineRefs[machine.machine][matfrom][i]) *
                            solution.GetVariableValue(mycube.machineRefs[machine.machine][matto][mystartstop[machine][i]]) *
                            mysimdata.mysetup[machine.machine][matfrom][matto].setupcost;

                        setuptime += solution.GetVariableValue(mycube.machineRefs[machine.machine][matfrom][i]) *
                            solution.GetVariableValue(mycube.machineRefs[machine.machine][matto][mystartstop[machine][i]]) *
                            mysimdata.mysetup[machine.machine][matfrom][matto].setuptime;*/
                    }

                    key = machine.machine + Convert.ToString(currentEpoch);
                    try
                    {
                        extra = exception[key];
                    }
                    catch (KeyNotFoundException)
                    {
                    }
                    solution.SetVariableValue(mysparetimes[key], extra + stoptime - setuptime);
                    prevEpoch = currentEpoch;
                }
            }
            return setupcost;
        }



        private double calcSales(COptQuestSolution solution)
        {

            double auxi = 0;
            double sales = 0;

            foreach (Materials mat in mysimdata.mymaterials.Values)
            {
                foreach (Epoch epo in mysimdata.myepochs.Values)
                {

                    // if(mat.demand.Contains<epo.epoch>;
                    if (mat.demand.ContainsKey(epo.epoch))
                        auxi += epo.duration * mat.demand[epo.epoch];
                }
                sales += mat.price * auxi;
                auxi = 0;
            }

            return sales;
        }

        private void stopStart(COptQuestSolution solution, Dictionary<String, Dictionary<int, StopStartEvent>> mystartstop, Dictionary<string, double> exception)
        {
            int i = 0;
            double usoi = 0;
            double usoii = 0;
            int start = 0;
            int stop = 0;
            string matstop = "";
            bool check = false;
            int currentEpoch = 0;
            foreach (String mach in mysimdata.mymachines.Keys)
            {
                mystartstop.Add(mach, new Dictionary<int, StopStartEvent>());

                foreach (DesignVariable var in mycube.getVarsForMachine(mach))
                {
                    i = var.time;
                    if (i < mysimdata.horizon - 1)
                    {
                        usoi = solution.GetVariableValue(var);
                        DesignVariable var2 = mycube.machineDesignInternal[mach][i + 1];

                        if (i < mysimdata.horizon)
                        {
                            usoii = solution.GetVariableValue(var2);
                        }

                        if (usoi != -1 && usoi == -1)
                        {
                            stop = i;
                            matstop = var.getMatkey(Convert.ToInt32(usoi));
                            check = true;
                        }
                        if (usoi == -1 && usoi != -1 && (stop != 0))
                        {
                            start = i + 1;
                            String matstart = var2.getMatkey(Convert.ToInt32(usoii));
                            StopStartEvent ssevent = new StopStartEvent(stop, start, matstop, matstart);
                            mystartstop[mach].Add(stop, ssevent);
                            check = false;
                        }
                    }
                    usoi = 0;
                    usoii = 0;
                }
                //Chequeo en qué época estoy
                foreach (int k in mysimdata.myepochs.Keys)
                {
                    if (i > mysimdata.myepochs[k].start && i <= (mysimdata.myepochs[k].start + mysimdata.myepochs[k].duration))
                    {
                        currentEpoch = k;
                    }
                }

                if (check == true)
                {
                    exception.Add(mach + currentEpoch, mysimdata.horizon - stop);
                    check = false;
                }
                stop = 0;
                start = 0;
            }

        }

        private double calcProdCost(COptQuestSolution solution)
        {

            double auxisuma = 0;

            double prodcost = 0;
            //Nueva version con el metodo que te devuelve todas las variables de una maquina con un material, se hace mas facil
            //y no tienen que entrar en el cubo

            foreach (MaterialProd matprod in mysimdata.mymaterialsprod.Values)
            {
                foreach (DesignVariable var in mycube.getVarsForMachineMaterial(matprod.machine.machine, matprod.material.material))
                {

                    // auxisuma += solution.GetVariableValue(var);
                    auxisuma += var.isMatProducing(solution, matprod.material);
                }

                prodcost += matprod.prodcost * matprod.prodrate * auxisuma;
                auxisuma = 0;


            }
            return prodcost;
        }


        //Calcluo de Hold Cost. Todo es al final de la hora.
        private void calcHoldBackCost(COptQuestSolution solution, out double holdcost, out double backordercost, out double servicelevel)
        {
            backordercost = 0;
            holdcost = 0;
            double stockinicial = 0;
            double stockfinal = 0;
            double backloginicial = 0;
            double backlogfinal = 0;
            double stockbackorder = 0;
            double sold = 0;
            double bom = 0;
            double rawmat = 0;
            double production = 0;
            double m = double.Epsilon;

            //double rawmatacum = 0;
            //double producedacum = 0;
            //double soldacum = 0;
            int lt = 0;
            string key;
            double diftargetraw = 0;
            diftarget = 0;

           
            double avgbodem = 0;
            servicelevel = 0;
            int l = 0;

            foreach (String mat_ini in mysimdata.mymaterials.Keys)
            {
                stockinicial = 0;
                stockfinal = 0;
                backloginicial = 0;
                backlogfinal = 0;
                stockbackorder = 0;

                SortedDictionary<int, double> mybacklogs = new SortedDictionary<int, double>();

                List<DesignVariable> mat_ini_list = mycube.getVarsForMaterial(mat_ini);
                foreach (DesignVariable dv in mat_ini_list)
                {
                    sold = 0;
                    rawmat = 0;
                    production = 0;

                    Epoch e = getEpochForI(dv.time);
                    int epochkey = e.epoch;
                    mybacklogs.ContainsKey(epochkey);
                    if (!mybacklogs.ContainsKey(epochkey))
                        mybacklogs.Add(epochkey, 0);



                    if (mysimdata.mymaterials[mat_ini].demand.ContainsKey(e.epoch))
                    {
                        sold = mysimdata.mymaterials[mat_ini].demand[e.epoch];
                        
                    }
                    else
                    {
                        sold = 0;
                    }

                    //soldacum += sold;
               

                    if (mysimdata.mybomcom.ContainsKey(mat_ini))
                        foreach (string mat_final in mysimdata.mybomcom[mat_ini].Keys)
                        {
                            List<DesignVariable> final_list = mycube.getVarsForMaterial_i(mat_final, dv.time);

                            foreach (DesignVariable dvf in final_list)
                            {
                                double vf = dvf.isMatProducing(solution, mat_final);
                                
                                    bom = mysimdata.mybomcom[mat_ini][mat_final];
                                    key = dvf.designMachine.machine.machine + mat_final;
                                    rawmat += bom * mysimdata.mymaterialsprod[key].prodrate * vf;
                                    
                                    //rawmatacum += rawmat;                             

                            }


                        }
                    key = dv.designMachine.machine.machine + mat_ini;
                    MaterialProd matprod = mysimdata.mymaterialsprod[key];
                    if (matprod.machine.stage == mysimdata.stages)
                        lt = 0;
                    else
                        lt = mysimdata.myleadtimes[matprod.machine.stage];
                    if (dv.time - lt > 0)
                    {
                        
                        double v = mycube.machineRefs[dv.designMachine.machine.machine][mat_ini][dv.time - lt].isMatProducing(solution, mat_ini);

                        production += matprod.prodrate * v;
                        
                        //producedacum += production;
                    }

                    stockbackorder = stockinicial - backloginicial + production - rawmat - sold;

                    stockfinal = Math.Max(0, stockbackorder);
                    backlogfinal = Math.Max(0, -stockbackorder);
                   

                    //Difference between target and stocks/backlogs at horizon for Target Inventory Cost
                    if (dv.time == Convert.ToInt32(mysimdata.horizon - 1))
                    {
                        diftargetraw = mysimdata.mymaterials[mat_ini].target - stockfinal + backlogfinal;
                        diftarget += Math.Max(0, diftargetraw);
                    }

                    //Dictionary for BOM req
                    key = mysimdata.mymaterials[mat_ini].material + dv.time;
                    myinventories.MergeSafe(key, stockfinal);

                    

                    
                    if (stockinicial == 0 || backloginicial == 0)
                    {
                        holdcost += mysimdata.mymaterials[mat_ini].holdingcost * ((stockfinal + stockinicial) * 0.5);

                        backordercost += mysimdata.mymaterials[mat_ini].backordercost * ((backlogfinal + backloginicial ) * 0.5);
                    }
                    if (stockfinal == 0 || backlogfinal == 0)
                    {
                        holdcost += mysimdata.mymaterials[mat_ini].holdingcost * ((stockinicial + stockfinal) / 2.0f);
                  
                        backordercost += mysimdata.mymaterials[mat_ini].backordercost * ((backloginicial + backlogfinal) / 2.0f);
                    }
                    else
                    {
                        holdcost += mysimdata.mymaterials[mat_ini].holdingcost * ((stockinicial + stockfinal) / 2.0f) *
                        ((stockinicial / (stockinicial + backlogfinal + m)) * (0.5f + 0.5f * (backlogfinal / (backlogfinal + m)))
                        + (stockfinal / (stockfinal + backloginicial + m)) * (0.5f + 0.5f * (backloginicial / (backloginicial + m))));

                        backordercost += mysimdata.mymaterials[mat_ini].backordercost * ((backloginicial + backlogfinal) / 2.0f) *
                        ((backlogfinal / (stockinicial + backlogfinal + m)) * (0.5f + 0.5f * (stockinicial / (stockinicial + m)))
                        + (backloginicial / (stockfinal + backloginicial + m)) * (0.5f + 0.5f * (stockfinal / (stockfinal + m))));
                    }


                    mybacklogs[epochkey] += Math.Max(backlogfinal-backloginicial, 0);
                                    

                    stockinicial = stockfinal;
                    backloginicial = backlogfinal;

                }
                           
                foreach (Epoch epo in mysimdata.myepochs.Values)
                {
                    if (mysimdata.mymaterials[mat_ini].demand.ContainsKey(epo.epoch))
                    {
                        if (mysimdata.mymaterials[mat_ini].demand[epo.epoch] > 0)
                        {
                            avgbodem += mybacklogs[epo.epoch] / (epo.duration * epo.duration * mysimdata.mymaterials[mat_ini].demand[epo.epoch]);
                        }
                        l = l + 1;  
                    }
                }

            }

            servicelevel = 1 - avgbodem;
       
        }


        private Epoch getEpochForI(int i)
        {
            foreach (Epoch e in mysimdata.myepochs.Values)
            {
                if (i >= (e.start) && i < (e.start + e.duration))
                    return e;
                //To solve round up problems at the end of horizon
                if (i >= mysimdata.horizon-1)
                    return e;
            }

            return null;
        }
       
        public double diftarget;

        private double calcTarInvCost(COptQuestSolution solution)
        {
            double tarinvcost;
            tarinvcost = mysimdata.invdeficitcost * diftarget;

            return tarinvcost;
        }


        /* public String describeYourself()
         {
             String output = "Im KCOptQuest\n";
             output += mycube.describeYourself();
             return output;
         }*/
    }
}
